// 日历组件
<template>
  <div class="calendar-wrap">
    <!-- 年月 -->
    <div class="calendar-title">
      {{ title }}
    </div>
    <el-divider></el-divider>
    <!-- 日历 -->
    <div class="calendar-content">
      <!-- 星期 -->
      <div class="g-flex-sa day-name">
        <div
          v-for="item in ['一','二','三','四','五','六','日']"
          :key="item"
        >{{ item }}
        </div>
      </div>
      <!-- 日期 -->
      <div class="g-flex-c day-num-wrap">
        <div v-for="item in gridNums" :key="item">
          <span :class="{'disabled': !checkDisable(item), 'checked': checkChoose(item)}" @click="chooseDate(item)" :id="item" :ref="`item${item}`">
            {{ item | computedDate(beginTime) }}
          </span>
        </div>
      </div>
    </div>
    <!-- 底部显示内容 -->
    <slot name="footer"></slot>
  </div>
</template>
<script>
import dayjs from 'dayjs'

export default {
  name: 'calendar',
  props: {
    range: Array,
    disabledDays: {
      type: Array,
      default () {
        return []
      }
    },
    maxLength: Number,
    time: Object,
    checkedDate: {
      type: Array,
      default () {
        return []
      }
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  model: {
    prop: 'time',
    event: 'change'
  },
  data () {
    return {
      range_c: [], // 显示在日历中的起止时间
      beginTime: '', // 显示在日历中的开始时间
      gridNums: 0, // 格数量
      startDay: null, // 传入的开始时间的day
      endDay: null, // 传入的结束时间的day
      chooseList: [], // 已选中日期
      chooseNum: [], // 已选中日子的num
    }
  },
  filters: {
    computedDate (val, beginTime) {
      return dayjs(beginTime).add(val - 1, 'day').format('MM-DD')
    }
  },
  computed: {
    title () {
      return `${dayjs().year()}年${dayjs().month() + 1}月`
    },
  },
  methods: {
    // 初始化日历，注：日历开始时间必须是周起始日，结束时间必须是周结束日。
    initCalendar () {
      // 日历初始时间，需要设置时分秒为00:00:00，否则计算num会出现小数问题
      this.range_c[0] = this.beginTime = this.formatDate(dayjs(dayjs(this.range[0]).format('YYYY-MM-DD 00:00:00')).valueOf(), 'start')
      this.range_c[1] = this.formatDate(+this.range[1], 'end')
      this.gridNums = Math.floor((+this.range_c[1] - +this.range_c[0]) / (24 * 3600 * 1000)) + 1
      // 设置传入时间外不可选的日期
      this.startDay = this.range[0].getDay() || 7
      this.endDay = this.gridNums - (7 - (this.range[1].getDay() || 7))
      // 反显选中日期
      if (this.checkedDate && this.checkedDate.length) {
        this.setDefaultChoose()
      }
    },
    formatDate (date, position) {
      const day = new Date(date).getDay() || 7
      if (position === 'start') {
        return new Date(date - (day - 1) * 24 * 3600 * 1000)
      } else {
        return new Date(date + (7 - day) * 24 * 3600 * 1000)
      }
    },
    // 计算某一格的日期
    transNumToDay (item) {
      return dayjs(this.beginTime).add(item - 1, 'day').format('MM-DD')
    },
    // 计算某一格的时间戳
    transNumToValue (item) {
      return dayjs(this.beginTime).add(item - 1, 'day').valueOf()
    },
    // 检查是否可选
    checkDisable (item) {
      return item >= this.startDay && item <= this.endDay && !this.disabledDays.includes(this.transNumToDay(item))
    },
    // 检查是否选中
    checkChoose (item) {
      return this.chooseList.includes(this.transNumToDay(item))
    },
    // 选择日期
    chooseDate (item) {
      // disabled为true代表只读
      if (this.disabled) return
      // 判断是否点击不可选的日期
      const className = this.$refs[`item${item}`][0].className
      if (className === 'disabled') return
      // 根据chooseList长度判断点击的是第几个格
      const length = this.chooseList.length
      let chooseRange // 选中的区域
      switch (length) {
        case 0:
          this.chooseList[0] = this.transNumToDay(item)
          this.chooseNum[0] = item
          this.setOneChoosed(item)
          this.emitData(item, item)
          break
        case 1:
          // 判断是否超过最大可选天数
          if (item - this.chooseNum[0] + 1 > this.maxLength || this.chooseNum[0] - item + 1 > this.maxLength) {
            this.$message({
              message: `活动时间不能大于剩余天数${this.maxLength}天`,
              type: 'warning'
            })
            this.clearChooseRange()
            this.chooseNum[0] = item
            this.setOneChoosed(item)
            this.emitData(item, item)
            return
          }
          chooseRange = [item, this.chooseNum[0]].sort((a, b) => a - b)
          // 判断范围内是否有不可用日期
          if (this.checkRangeDisable(...chooseRange)) {
            this.clearChooseRange()
            this.chooseList[0] = this.transNumToDay(item)
            this.chooseNum[0] = item
            this.setOneChoosed(item)
            this.emitData(this.chooseNum[0], this.chooseNum[0])
            return
          }
          // 区间可用，则设置区间选中
          this.chooseNum = chooseRange
          this.chooseList = [this.transNumToDay(chooseRange[0]), this.transNumToDay(chooseRange[1])]
          this.clearChooseRange()
          this.setChooseRange()
          this.emitData(...this.chooseNum)
          break
        case 2:
          this.chooseList = []
          this.chooseNum = []
          this.clearChooseRange()
          this.chooseList[0] = this.transNumToDay(item)
          this.chooseNum[0] = item
          this.setOneChoosed(item)
          this.emitData(item, item)
          break
      }
    },
    // 检查区间内是否有不可选的格子
    checkRangeDisable (item0, item1) {
      for (let i = item0; i < item1; i++) {
        const className = this.$refs[`item${i}`][0].className
        if (className === 'disabled') {
          return true
        }
      }
      return false
    },
    // 清除所有已选中
    clearChooseRange () {
      for (let i = 1; i <= this.gridNums; i++) {
        const className = this.$refs[`item${i}`][0].className
        this.$refs[`item${i}`][0].className = this.$refs[`item${i}`][0].className === 'checked' ? '' : className
      }
    },
    // 设置区间内选中
    setChooseRange () {
      for (let i = this.chooseNum[0]; i <= this.chooseNum[1]; i++) {
        this.setOneChoosed(i)
      }
    },
    // 设置某一格子被选中
    setOneChoosed (item) {
      this.$refs[`item${item}`][0].className = 'checked'
    },
    // 派发数据给父组件
    emitData (item0, item1) {
      this.$emit('change', {
        startTime: this.transNumToValue(item0),
        endTime: this.transNumToValue(item1)
      })
    },
    // 清除已选中的list
    clearChooseList () {
      this.chooseList = []
      this.chooseNum = []
      this.clearChooseRange()
      this.$emit('change', {
        startTime: null,
        endTime: null
      })
    },
    // 反显已选中的日期
    setDefaultChoose () {
      const startTime = Math.floor((+this.checkedDate[0] - +this.range_c[0]) / (24 * 3600 * 1000)) + 1
      const endTime = Math.floor((+this.checkedDate[1] - +this.range_c[0]) / (24 * 3600 * 1000)) + 1
      this.chooseNum = [startTime, endTime]
      this.chooseList = [this.transNumToDay(startTime), this.transNumToDay(endTime)]
      this.$nextTick(() => {
        this.setChooseRange()
      })
    }
  }
}
</script>
<style lang="less" scoped>
  .calendar-title {
    text-align: center;
    color: #222222;
    font-size: 16px;
    font-weight: 600;
  }
  .day-num-wrap {
    flex-wrap: wrap;
    text-align: center;
    &>div {
      flex: 0 0 calc(100% / 7);
      span {
        display: inline-block;
        width: 60px;
        height: 50px;
        line-height: 50px;
        border-radius: 6px;
        border: 1px solid #E3E3E3;
        margin-top: 13px;
        cursor: pointer;
      }
      .disabled {
        background: #FAFAFA;
        color: #D4D4D4;
      }
      .checked {
        background: #4B7EFE;
        color: #fff;
      }
    }
  }
</style>